#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "xmalloc.h"
#include "template_simple.h"
#include "scanner.h"
#include "tag.h"
#include "hashmap.h"


static const char *tag_name = STMPL_TAG;
const tmpl_t smpl_tpl = { STMPL_TAG, template_simple_preinit, template_simple_init, template_simple_free, template_simple_cleanup, template_simple_setval, template_simple_getptr, template_simple_result, template_simple_request };
static tmpl_data_t *data;
__NCTLS hashmap_t vals = NULL;


static data_elem_t *
new_elem(const char *key, size_t pos)
{
  data_elem_t *te;

  te = (data_elem_t *) ncmalloc(1, sizeof(data_elem_t));
  te->idx = pos;
  if (key) { te->key = strdup(key); } else { te->key = NULL; }
  te->next = NULL;
  return (te);
}

static void
free_elem(data_elem_t *te)
{
  if (!te) return;
  if (te->key) free(te->key);
  ncfree(te, sizeof(data_elem_t));
}

static void
parse_templates(tmpl_data_t *tmpl_data)
{
  int retval;
  size_t tag_len = 0;
  char *tag_offset, *start_tag, *attr_offset;
  string_t *tag = (string_t *) create_empty_string();
  string_t *data = tmpl_data->data;
  string_t *file = tmpl_data->path;
  data_elem_t *de, *dt = NULL;

  dt = new_elem(NULL, 0);
  de = dt;
  tag_offset = (char *) get_string(data);
  while ( (retval = scan_tag(tag_name, &tag_offset, &start_tag, &tag_len, tag, SCANFLG_TAGONLY)) != SCANERR_NOTFOUND) {
    attr_offset = (char *)(get_string(tag) + get_strlen(tag));
    switch (retval) {
      case SCANERR_SIMPLETAG:
        if ( scan_tag_attr(&attr_offset, NULL) != SCANERR_NOTFOUND ) {
          W("%s: no attributes allowed in template files", get_string(file));
          print_str_cutout(data, tag_offset, CUTOUT_WIDTH);
        } else {
          size_t idx = (start_tag - get_string(data));
          de->next = new_elem(get_string(tag), idx);
	  de = de->next;
        }
        break;
      case SCANERR_NOVALID:
        W("%s: no attributes allowed in template files", get_string(file));
        print_str_cutout(data, tag_offset, CUTOUT_WIDTH);
        break;
      case SCANERR_OK:
        W("%s: only simpletags are allowed in template files", get_string(file));
        print_str_cutout(data, tag_offset, CUTOUT_WIDTH);
        break;
    }
    remove_tag(data, start_tag, tag_len);
    tag_offset = start_tag;
  }
  free_string(tag);
  tmpl_data->__unused = dt;
}

int
template_simple_preinit(void)
{
  tmpl_data_t *tmp = data = read_template_files(STMPL_FSUFFIX);
  
  if (!data) return (TMPLERR_NOFILES);
  while (tmp->path && tmp->data) {
    parse_templates(tmp);
    tmp = tmp->next;
  }
  return (TMPLERR_OK);
}

int
template_simple_init(void)
{
  vals = hashmap_nnew(STMPL_HMAPSIZ);
  return ((vals ? TMPLERR_OK : TMPLERR_FAILED));
}

static void clear_values_map(void)
{
  int idx = 0;
  char *key;
  string_t *val;

  while (hashmap_iterate_all(vals, &idx, &key, (any_t *) &val)) {
    if (key && val) {
      free_string(val);
      hashmap_remove(vals, key);
    }
  }
}

int
template_simple_cleanup(void)
{
  clear_values_map();
  hashmap_free(vals);
  return (TMPLERR_OK);
}

int
template_simple_free(void)
{
  tmpl_data_t *dt;
  data_elem_t *cur, *next;

  if (!data) return (TMPLERR_NOFILES);

  dt = data;
  while (dt->path && dt->data) {
    cur = (data_elem_t *) dt->__unused;
    while (cur) {
      next = cur->next;
      free_elem(cur);
      cur = next;
    }
    dt = dt->next;
  }
  cleanup_template(data);
  return (TMPLERR_OK);
}

int
template_simple_setval(const tmpl_data_t *td __attribute__((__unused__)), const char *key, const char *value)
{
  string_t *val;

  switch (hashmap_get(vals, (char *) key, (any_t *) &val)) {
    case MAP_OK:
      set_string(val, value);
      return (TMPLERR_OK);
    break;
    case MAP_MISSING:
      val = create_string(value);
      if (hashmap_put(vals, (char *) key, val) != MAP_OK) {
        free_string(val);
        return (TMPLERR_FAILED);
      } else return (TMPLERR_OK);
    break;
  }
  return (TMPLERR_FAILED);
}

tmpl_data_t *
template_simple_getptr(const char *filename)
{
  char *loc;
  tmpl_data_t *dt;

  if (!data) return (NULL);
  dt = data;
  while (dt->path && dt->data) {
    asprintf(&loc, "%s/%s.%s", tmpl_dir, filename, tag_name);
    if (strcmp(loc, get_string(dt->path)) == 0) {
      free(loc);
      return (dt);
    }
    free(loc);
    dt = dt->next;
  }
  return (NULL);
}

string_t *
template_simple_result(const tmpl_data_t *td, string_t **str_ptr)
{
  size_t rlen, rpos = 0;
  data_elem_t *de = (data_elem_t *) td->__unused;
  string_t *result, *val = NULL;

  if (str_ptr && *str_ptr) {
    result = *str_ptr;
    rpos += get_strlen(result);
    add_string(result, get_string(td->data));
  } else {
    result = create_dupstring(td->data);
  }
#ifdef ENABLE_SMPLDBG
  print_hashmap_debug("TMPL_SIMPLE values", vals);
#endif
  while ((de = de->next) != NULL) {
    if (hashmap_get(vals, de->key, (any_t *) &val) == MAP_OK) {
      if (val) {
        rlen = get_strlen(val);
        insert_string(result, de->idx + rpos, get_string(val), rlen);
        rpos += rlen;
	set_string(val, "");
      }
    }
  }
  return (result);
}

void
template_simple_request(const tmpl_data_t *td, const request_t *req)
{
  template_simple_setval(td, KEY_QRY, req->qry);
  template_simple_setval(td, KEY_URI, req->doc_uri);
}
